package com.tungse.dynamic;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Author : Tungse
 * @Date: 2025/9/1 15:17
 * @Description:
 */
@SpringBootApplication
@MapperScan("com.tungse.dynamic.mapper")
public class DynamicApplication {

    public static void main(String[] args) {
         SpringApplication.run(DynamicApplication.class, args);
    }
}

//| 场景分类 | 调用链示例 | 数据源切换是否有效？ | 原因分析及说明 |
//| :--- | :--- | :--- | :--- |
//| **1. 无事务调用链** | `Controller -> AService.findX() -> BService.insertY()` | **✅ 有效** | 这是**最理想**的情况。每个Service方法调用都经过代理，AOP切面都能正确拦截并根据方法名设置数据源上下文。每个DAO操作都会在需要连接时，根据**当前线程的上下文**去获取对应的主库或从库连接。 |
//| **2. 全REQUIRES_NEW调用链** | `Controller -> AService.methodA()` <br> （`@Transactional(REQUIRES_NEW)`） <br> `-> BService.methodB()` <br> （`@Transactional(REQUIRES_NEW)`） | **✅ 有效** | 每个`REQUIRES_NEW`都会**挂起**现有事务并**创建一个新事务**。创建新事务意味着要**重新获取一个新的数据库连接**。在获取这个新连接时，会调用`determineCurrentLookupKey()`方法，而此时AOP切面已经根据**当前执行的方法名**设置了正确的数据源上下文，因此能获取到正确的连接。 |
//| **3. 外部有事务，内部REQUIRES_NEW** | `Controller -> AService.insertX()` <br> （`@Transactional`） <br> `-> BService.selectY()` <br> （`@Transactional(REQUIRES_NEW)`） | **✅ 内部有效** | **外部事务**：从事务管理器获取连接时，AOP根据`insertX`设置为**主库**连接。 <br> **内部REQUIRES_NEW**：挂起外部事务，创建新事务。此时AOP根据`selectY`将上下文切为**从库**。获取新连接时，新连接来自从库。方法执行完后，上下文恢复，外部事务继续使用主库连接。 |
//| **4. 经典事务陷阱（常见）** | `Controller -> AService.insertX()` <br> （`@Transactional`） <br> `-> BService.selectY()` <br> （**无事务注解**） | **❌ 内部无效** | **外部事务**：在进入`insertX`时，事务管理器就根据AOP设置的**主库**上下文获取了一个主库连接，并绑定到当前线程。 <br> **内部调用**：虽然调用`selectY`时，AOP切面会执行并将上下文设置为**从库**。但当DAO操作需要连接时，事务管理器发现当前线程已绑定了一个活跃的事务连接（主库），便**直接重用**它，而不会重新获取新连接。导致查询也在主库执行。 |
//| **5. 同类内部调用（AOP失效）** | `AService.insertX()` { <br> &nbsp; `this.selectY();` <br>} | **❌ 彻底失效** | **最严重**的问题。`this`指向真实对象而非代理对象，因此`selectY`方法的调用**完全绕过了AOP代理**。数据源上下文不会被切换，`selectY`方法会使用`insertX`方法设置的或默认的数据源。 |
//| **6. 混合操作于同一事务** | `AService.complexOper()` { <br> &nbsp; `dao.select();` <br> &nbsp; `dao.insert();` <br>} <br> （`@Transactional`） | **❌ 写操作危险** | 在方法开始时，事务管理器根据`complexOper`的方法名（通常不匹配切点，故使用默认数据源，很可能是**主库**）获取连接。整个方法内的所有数据库操作，无论读写，都使用这**同一个主库连接**。如果方法名意外匹配了`readPointcut`，会导致所有写操作也走到从库，造成灾难性后果。 |
//
//---